﻿#region

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Linq;
using System.Linq.Expressions;
using XXS.Common.BaseModel;

#endregion

namespace XXS.Common.BaseDataAccessLayer
{
    /// <summary>
    ///     数据库底层操作类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class BaseDAL<T> : IBaseDAL<T> where T : class, IBaseModel, new()
    {
        /// <summary>
        ///     ORM 映射
        /// </summary>
        private DbContext _db;

        private bool _isDisposed;

        public BaseDAL(DbContext db)
        {
            _db = db;
        }

        /// <summary>
        ///     在基类操作中,立即调用 Save;
        /// </summary>
        protected bool IsSaveImmediately { get; set; } = true;

        /// <summary>
        ///     当前数据库连接字符串
        /// </summary>
        public string ConnectionString => _db.Database.Connection.ConnectionString;

        public DbContext DbContext
        {
            get { return _db; }

            set { _db = value; }
        }

        /// <summary>
        ///     按照λ表达式获取记录集数量
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public int Count(Expression<Func<T, bool>> predicate)
        {
            return _db.Set<T>().Count(predicate);
        }

        /// <summary>
        ///     按照λ表达式确认是否在数据库中存在
        /// </summary>
        /// <param name="anyLambda"></param>
        /// <returns></returns>
        public bool Exist(Expression<Func<T, bool>> anyLambda)
        {
            return _db.Set<T>().Any(anyLambda);
        }

        /// <summary>
        ///     按照λ表达式确认是否在数据库中不存在
        /// </summary>
        /// <param name="anyLambda"></param>
        /// <returns></returns>
        public bool NotExist(Expression<Func<T, bool>> anyLambda)
        {
            return !Exist(anyLambda);
        }

        /// <summary>
        ///     按照λ表达式获取数据集分页列表
        /// </summary>
        /// <typeparam name="TS">排序类型</typeparam>
        /// <param name="whereLamdba">where的λ表达式</param>
        /// <param name="orderLamdba">orderby的λ表达式</param>
        /// <param name="isAsc">是否是否正序排列</param>
        /// <param name="pageIndex">分页页码</param>
        /// <param name="pageSize">分页大小</param>
        /// <param name="totalRecord">记录集总数</param>
        /// <returns></returns>
        public IQueryable<T> FindPagenatedList<TS>(Expression<Func<T, bool>> whereLamdba,
            Expression<Func<T, TS>> orderLamdba, bool isAsc, int pageIndex, int pageSize, out int totalRecord)
        {
            if (pageIndex < 1)
                throw new ArgumentOutOfRangeException(nameof(pageIndex));

            if (pageSize < 0)
                throw new ArgumentOutOfRangeException(nameof(pageSize));

            totalRecord = Count(whereLamdba);
            return (isAsc
                ? FindList(whereLamdba).OrderBy(orderLamdba)
                : FindList(whereLamdba).OrderByDescending(orderLamdba)).Skip(pageIndex - 1).Take(pageSize);
        }
        /// <summary>
        ///     获取数据集列表
        /// </summary>
        /// <param name="tracking">是否跟踪记录集</param>
        /// <param name="lazy">是否延迟加载</param>
        /// <returns></returns>
        public IQueryable<T> FindList()
        {
            return FindList(null, null, true, true);
        }

        /// <summary>
        ///     获取数据集列表
        /// </summary>
        /// <param name="tracking">是否跟踪记录集</param>
        /// <param name="lazy">是否延迟加载</param>
        /// <returns></returns>
        public IQueryable<T> FindList(bool tracking = true, bool lazy = true)
        {
            return FindList(null, null, tracking, lazy);
        }

        /// <summary>
        ///     按照λ表达式获取数据集列表
        /// </summary>
        /// <param name="includings">包含数据表</param>
        /// <param name="tracking">是否跟踪记录集</param>
        /// <param name="lazy">是否延迟加载</param>
        /// <returns></returns>
        public IQueryable<T> FindList(string[] includings = null,
            bool tracking = true,
            bool lazy = true)
        {
            return FindList(null, includings, tracking, lazy);
        }

        /// <summary>
        ///     按照λ表达式获取数据集列表
        /// </summary>
        /// <param name="whereLamdba">where的λ表达式</param>
        /// <param name="includings">包含数据表</param>
        /// <param name="tracking">是否跟踪记录集</param>
        /// <param name="lazy">是否延迟加载</param>
        /// <returns></returns>
        public IQueryable<T> FindList(Expression<Func<T, bool>> whereLamdba, string[] includings = null,
            bool tracking = true,
            bool lazy = true)
        {
            _db.Configuration.LazyLoadingEnabled = lazy;

            DbQuery<T> dbset = _db.Set<T>();

            includings?.ToList().ForEach(n => dbset = dbset.Include(n));

            dbset = tracking ? dbset : dbset.AsNoTracking();


            return whereLamdba == null ? dbset : dbset.Where(whereLamdba);
        }


        public IQueryable<TR> FindList<TR>(Expression<Func<TR, bool>> whereLamdba, string[] includings = null,
            bool tracking = true,
            bool lazy = true) where TR : class, T
        {
            _db.Configuration.LazyLoadingEnabled = lazy;

            DbQuery<TR> dbset = _db.Set<TR>();

            includings?.ToList().ForEach(n => dbset.Include(n));

            dbset = tracking ? dbset : dbset.AsNoTracking();


            if (whereLamdba == null)
            {
                return dbset;
            }

            return dbset.Where(whereLamdba);
        }

        /// <summary>
        ///     直接存储
        /// </summary>
        public void Save()
        {
            if (IsSaveImmediately)
            {
                try
                {
                    _db.SaveChanges();
                }
                catch (DbEntityValidationException ex)
                {
                    var error = ex.EntityValidationErrors.Aggregate(string.Empty
                        , (current1, item) => item.ValidationErrors.Aggregate(current1
                            , (current, item2) => current + $"{item2.PropertyName}:{item2.ErrorMessage}<br/>"));

                    throw new Exception(error, ex);
                }
            }
        }

        /// <summary>
        ///     为真则立即存储
        /// </summary>
        /// <param name="immediately">存储条件:true直接存储false依据IsSaveImmediately状态存储,存储完毕后并更改 IsSaveImmediately 属性值</param>
        public void Save(bool immediately)
        {
            if (!immediately)
                Save();
            else
            {
                _db.SaveChanges();
                IsSaveImmediately = true;
            }
        }

        /// <summary>
        ///     对本次Save做延迟处理,直到调用Save(true)结束
        /// </summary>
        public void SaveLater()
        {
            IsSaveImmediately = false;
        }

        /// <summary>
        ///     销毁该类
        /// </summary>
        /// <param name="isDisposing"></param>
        public virtual void Dispose(bool isDisposing)
        {
            if (_isDisposed) return;
            if (isDisposing)
            {
                _db.Dispose();
                _db = null;
            }
            _isDisposed = true;
        }

        #region ICRUD<T> Members

        /// <summary>
        ///     在数据库中按照id查询一条记录,并返回.
        /// </summary>
        /// <param name="id">数据库主键</param>
        /// <returns></returns>
        public T Find(object id)
        {
            return _db.Set<T>().Find(id);
        }

        /// <summary>
        ///     在数据库中按照λ表达式查找一条记录,并返回
        /// </summary>
        /// <param name="predicate">查询λ表达式</param>
        /// <returns></returns>
        public T Find(Expression<Func<T, bool>> predicate)
        {
            return _db.Set<T>().FirstOrDefault(predicate);
        }

        /// <summary>
        ///     在数据库中按照λ表达式查找一条记录,并返回
        /// </summary>
        /// <param name="predicate">查询λ表达式</param>
        /// <param name="includeStrings">导航属性</param>
        /// <returns></returns>
        public T Find(Expression<Func<T, bool>> predicate, string[] includeStrings)
        {
            var source = _db.Set<T>() as DbQuery<T>;

            includeStrings?.ToList().ForEach(n => source = source.Include(n));

            return source.FirstOrDefault(predicate);
        }

        /// <summary>
        ///     在模型未持久化前,查找原始数据.
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public DbPropertyValues FindOriginalValues(T model)
        {
            return _db.Entry(model).OriginalValues;
        }

        /// <summary>
        ///     在模型未持久化前,查找当前数据.
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public DbPropertyValues FindCurrentValues(T model)
        {
            return _db.Entry(model).CurrentValues;
        }

        /// <summary>
        ///     获取当前的模型验证错误
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public DbEntityValidationResult GetValidationErrors(T model)
        {
            return _db.Entry(model).GetValidationResult();
        }

        /// <summary>
        ///     删除一个模型
        /// </summary>
        /// <param name="model"></param>
        public virtual void Delete(T model)
        {
            if (model == null)
                return;
            _db.Entry(model).State = EntityState.Deleted;
            Save();
        }

        /// <summary>
        ///     创建一个模型
        /// </summary>
        /// <param name="model"></param>
        /// <param name="newId">是否重新生成id</param>
        /// <param name="reloadModelFromDb">是否重新从数据库中读取数据,如果您需要级联数据加载,请设置为true</param>
        /// <returns></returns>
        public virtual T Create(T model, bool newId = true, bool reloadModelFromDb = false)
        {
            if (newId)
                //model.Id = Guid.NewGuid();
            if (model.CreateDateTime == DateTime.MinValue)
            {
                model.CreateDateTime = DateTime.Now;
            }
            _db.Set<T>().Add(model);
            Save();

            if (reloadModelFromDb)
            {
                _db.Entry(model).State = EntityState.Detached;
                return Find(model.Id);
            }

            return model;
        }

        /// <summary>
        ///     更新一个模型
        /// </summary>
        /// <param name="model"></param>
        /// <param name="refreshUpdateTime"></param>
        /// <param name="reloadModelFromDb">是否重新从数据库中读取数据,如果您需要级联数据加载,请设置为true</param>
        /// <returns></returns>
        public virtual T Update(T model, bool refreshUpdateTime = true, bool reloadModelFromDb = false)
        {
            if (refreshUpdateTime)
                model.UpdateDateTime = DateTime.Now;

            if (_db.Entry(model).State == EntityState.Detached)
            {
                HandleDetached(model);
            }

            _db.Entry(model).State = EntityState.Modified;
            Save();

            if (reloadModelFromDb)
            {
                _db.Entry(model).State = EntityState.Detached;
                return Find(model.Id);
            }

            return model;
        }

        private bool HandleDetached(T entity)
        {
            var objectContext = ((IObjectContextAdapter)_db).ObjectContext;
            var entitySet = objectContext.CreateObjectSet<T>();
            var entityKey = objectContext.CreateEntityKey(entitySet.EntitySet.Name, entity);
            object foundSet;
            bool exists = objectContext.TryGetObjectByKey(entityKey, out foundSet);
            if (exists)
            {
                objectContext.Detach(foundSet); //从上下文中移除
            }
            return exists;
        }

        /// <summary>
        ///     销毁当前类
        /// </summary>
        public void Dispose()
        {
            Dispose(true);

            GC.SuppressFinalize(this);
        }

        #endregion
    }
}